use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.processor_pkg.all;

entity fifo is

	port(	in_clk		:	in	std_logic;
			in_rst		:	in	std_logic;
			
			in_data		:	in	fifo_in_data_t;
			in_control	:	in	fifo_in_control_t;
			
			out_data		:	out	fifo_out_data_t;
			out_control	:	out	fifo_out_control_t
	);
	
end entity fifo;

architecture arch of fifo is
	
	constant N:	natural	:= 6;
	
	type content_t is array (0 to N-1) of fifo_in_data_t;
	
	signal head_reg, head_next	:	std_logic_vector(N-1 downto 0);
	signal tail_reg, tail_next	:	std_logic_vector(N-1 downto 0);
	signal content_reg, content_next	:	content_t;
	signal full_reg, full_next	:	std_logic;
begin
	clk:process(in_clk, in_rst) is
	begin
		if(in_rst) then
			head_reg	<=	(others => '0');
			tail_reg	<=	(others => '0');
			full_reg	<=	(others => '0');
		elsif(rising_edge(in_clk)) then
			head_reg	<=	head_next;
			tail_reg	<=	tail_next;
			content_reg	<=	content_next;
			full_reg	<=	full_next;
		end if;
	end process clk;
	
	new_data:process(in_data) is
	variable	tail_var	:	std_logic_vector(N-1 downto 0);
	begin
		tail_var	:=	tail_reg;
		for i in in_data.instructions'range loop
			if(in_data.instructions(i).valid = '1') then
				content_next(tail_var)	<=	in_data.instructions(i);
				tail_var	:=	unsigned_add_modulo(tail_var, 1, N);
				full_next <= '1'
					when (((unsigned)tail_var = (unsigned)head_next) or (((unsigned)tail_var - (unsigned)head_next)>N-ISSUE_WIDTH) or ((unsigned)head_next - (unsigned)tail_var < ISSUE_WIDTH) )
				else full_next <= '0';
			end if;
		end loop;
		tail_next	<=	tail_var;
	end process new_data;
	
	out_data.instructions(0)	<=	content_reg(unsigned(head_reg));
	out_data.instructions(1)	<=	content_reg(unsigned(unsigned_add_modulo(head_reg,1,N)));
	
	ctrl:process(in_control) is
	variable head_var	:	std_logic_vector(N-1 downto 0);
	begin
		head_var	:=	head_reg;
		if(in_control.taken1 = '1') then
			head_var	:=	unsigned_add_modulo(head_reg, 1, N);			
		elsif (in_control.taken2 = '1') then
			head_var	:=	unsigned_add_modulo(head_reg, 2, N);	
		end if;
		head_next	<=	head_var;
		
		full_next <= '0'
			when (((unsigned)tail_var = (unsigned)head_next) or (((unsigned)tail_var - (unsigned)head_next)>N-ISSUE_WIDTH) or ((unsigned)head_next - (unsigned)tail_var < ISSUE_WIDTH) )
		else full_next <= '1';
		
		
		if(in_control.flush = '1') then
			head_next	<=	(others => '0');
			tail_next	<=	(others => '0');
		end if;
	end process ctrl;
	
	out_control.stall	<=	full_reg;
end architecture arch;